﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace Fysh.ComputerScience.Genetics
{
    public class TreeGenerator
    {
        private float _minBranchLength = 0f;
        private float _maxBranchLength = 1f;

        /// <summary>
        /// List of hashes of all generated sequences from object instanciation
        /// </summary>
        public System.Collections.Generic.List<string> GeneratedTrees
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }

        /// <summary>
        /// Maximum whole length of any branch generated for a tree
        /// </summary>
        public int MaxBranchLength
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }

        /// <summary>
        /// Minimum whole length of any branch generated for a tree
        /// </summary>
        public int MinBranchLength
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }

        /// <summary>
        /// This is the minimal distance between two branches of different lengths
        /// </summary>
        public float BranchLengthStep
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }
        /// <summary>
        /// Randomly produces a tree based on passed tree
        /// </summary>
        public PhylogeneticTree NextTree(SequenceCollection sequenceCollection)
        {
            Random randomNumberGenerator = new Random();

            List<Node> constructionList = new List<Node>();
            List<Node> nodeList = new List<Node>();

            //assign each sequence in the collection to a node
            foreach (string sequenceName in sequenceCollection.Keys)
            {
                Node newNode = new Node();
                newNode.CurrentNode = sequenceCollection[sequenceName];
                constructionList.Add(newNode);
                nodeList.Add(newNode);
            }
            //iteratively construct the tree until only the root node exists
            while (constructionList.Count > 1)
            {
                int randomIndex = randomNumberGenerator.Next(0, constructionList.Count);
                //get the first node to pair
                Node nodeA = constructionList[randomIndex];
                constructionList.RemoveAt(randomIndex);
                //get the second node to pair
                randomIndex = randomNumberGenerator.Next(0, constructionList.Count);
                Node nodeB = constructionList[randomIndex];
                constructionList.RemoveAt(randomIndex);
                //create new node to reference the removed pair
                Node parentNode = new Node();
                parentNode.ChildA = nodeA;
                parentNode.ChildB = nodeB;
                //set branches to the children
                parentNode.LengthA = (float)((double)randomNumberGenerator.Next((int)_minBranchLength, (int)_maxBranchLength) * randomNumberGenerator.NextDouble());
                parentNode.LengthB = (float)((double)randomNumberGenerator.Next((int)_minBranchLength, (int)_maxBranchLength) * randomNumberGenerator.NextDouble());
                //insert parent node into the node list
                nodeList.Add(parentNode);
                //insert the node back into the list
                constructionList.Add(parentNode);
            }

            //set the properties of the tree
            PhylogeneticTree randomTree = new PhylogeneticTree();
            randomTree.SpeciesCollection = sequenceCollection;
            randomTree.RootNode = constructionList[0];
            randomTree.Nodes = nodeList.ToArray();

            return randomTree;
        }

        /// <summary>
        /// Shuffles the topology of a passed tree whilst maintaining the order the species appear in the tree
        /// </summary>
        public PhylogeneticTree ShuffleTopology()
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// Shuffles the order to which species appear in a passed tree whilst maintaining the topology
        /// </summary>
        public PhylogeneticTree ShuffleSpecies()
        {
            throw new System.NotImplementedException();
        }

        /// <param name="contributionRatio">The ratio of how much each parent contributes to the offspring</param>
        public PhylogeneticTree MergeTrees(PhylogeneticTree ParentA, PhylogeneticTree ParentB, double contributionRatio)
        {
            throw new System.NotImplementedException();

            double[] rouletteWheel = new double[ParentA.Nodes.Length];

            //calculate branch selection probabilities
            for (int i = 0; i < ParentA.Nodes.Length; i++)
                rouletteWheel[i] = Math.Exp(-(1 / ParentA.Nodes[i].ChildrenCount)) * contributionRatio;

            //sum selection probabilities to produce partitions
            for (int i = 1; i < ParentA.Nodes.Length; i++)
                rouletteWheel[i] += rouletteWheel[i - 1];

            //generate selection probability
            double selectionProbability = new Random().NextDouble();

            int nodeSelectionIndex = 0;

            //locate where the selection probability exists in the roulette wheel
            while (selectionProbability > rouletteWheel[nodeSelectionIndex]) { nodeSelectionIndex++; };

            //copy the subtree from the parent
            Node subtreeRoot = (Node)ParentA.Nodes[nodeSelectionIndex].Clone();
            
        }

    }
}
